package com.luckyframe.project.jmeter.consumer.watch;

import com.alibaba.fastjson.JSON;
import com.luckyframe.common.utils.spring.SpringUtils;
import com.luckyframe.project.jmeter.consumer.SampleHandler;
import com.luckyframe.project.jmeter.jmeterTestPlan.controller.JmeterTestPlanController;
import com.luckyframe.project.jmeter.jmeterTestPlan.domain.JmeterTestPlan;
import com.luckyframe.project.jmeter.jmeterTestPlanMetric.domain.JmeterTestPlanMetric;
import com.luckyframe.project.jmeter.jmeterTestPlanMetric.enums.PlanMetricStatusEnum;
import com.luckyframe.project.jmeter.jmeterTestPlanMetric.service.IJmeterTestPlanMetricService;
import com.luckyframe.project.jmeter.report.domain.Chart;
import org.apache.jmeter.report.core.Sample;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.BlockingQueue;

/**
 * task execute per second to accumulative consume samples
 */
public class PlanWatchTask implements Runnable {

    private static final Logger logger = LoggerFactory.getLogger(JmeterTestPlanController.class);

    private SampleHandler sampleHandler = new SampleHandler();

    private JmeterTestPlan plan;

    private JmeterTestPlanMetric metric;

    private BlockingQueue<Sample> queue;

    private IJmeterTestPlanMetricService planMetricService = SpringUtils.getBean(IJmeterTestPlanMetricService.class);

    private List<Sample> samplesTobeConsumed = new ArrayList<>();

    private List<Sample> totalSamples = new ArrayList<>();

    private long lastSendTime = -99999L;

    private int prevSize=0;

    private volatile boolean running = true;

    public PlanWatchTask(JmeterTestPlan plan, JmeterTestPlanMetric metric, BlockingQueue<Sample> queue) {
        this.plan = plan;
        this.queue = queue;
        this.metric = metric;
    }


    @Override
    public void run() {

        try {
            while (running) {
                if (consumeSamples()) continue;
            }

        } catch (Exception e) {
            logger.error("PlanWatchTask error occurred plan={}", plan.getName(),e);
        }
    }

    private boolean consumeSamples() {

        Sample sample = queue.poll();

        if(sample==null){
            return true;
        }

        samplesTobeConsumed.add(sample);
        totalSamples.add(sample);

        //超过1s
        if (System.currentTimeMillis() - lastSendTime > 1000) {

            if (totalSamples.size() > 0 && totalSamples.size()>prevSize) {

                logger.info("metric {} plan {} get sample time={} size={}",metric.getSn(), plan.getName(), LocalDateTime.now(), totalSamples.size());

                List<Chart> charts = sampleHandler.generateCharts(totalSamples);

                JmeterTestPlanMetric planMetric = planMetricService.selectJmeterTestPlanMetricById(this.metric.getId());

                planMetric.setResultData(JSON.toJSONString(charts));

                JmeterTestPlanMetric metricData=null;
                try {

                    metricData= sampleHandler.generatePlanMetric(totalSamples);
                }catch (Exception ex){
                    fail(ex);
                }

                if (metricData != null) {

                    planMetric.setTps(metricData.getTps());
                    planMetric.setAvgRespTime(metricData.getAvgRespTime());
                    planMetric.setErrorRatio(metricData.getErrorRatio());
                    planMetric.setHits(metricData.getHits());
                }

                logger.info("metric {} plan {} get data {}",metric.getSn(), plan.getName(), JSON.toJSONString(metricData));

                planMetric.setStartTime(new Date(totalSamples.get(0).getStartTime()));
                planMetricService.updateJmeterTestPlanMetric(planMetric);

                prevSize=totalSamples.size();
            }

            lastSendTime = System.currentTimeMillis();
        }
        return false;
    }

    public void start() {
        running = true;
        JmeterTestPlanMetric planMetric = planMetricService.selectJmeterTestPlanMetricById(this.metric.getId());
        if(planMetric!=null ){
            planMetric.setStartTime(new Date());
            planMetric.setStatus(PlanMetricStatusEnum.EXECUTING.getCode());
            planMetricService.updateJmeterTestPlanMetric(planMetric);
        }
    }

    public void stop() {

        logger.info("metric {} plan {} end callback start @ {}", metric.getSn(),plan.getName(), LocalDateTime.now());

        try {

            running = false;

            consumeSamples();

            JmeterTestPlanMetric planMetric = planMetricService.selectJmeterTestPlanMetricById(this.metric.getId());
            if(planMetric!=null && totalSamples.size()>0){
                planMetric.setEndTime(new Date(totalSamples.get(totalSamples.size()-1).getEndTime()));
                planMetric.setStatus(PlanMetricStatusEnum.SUCCESS.getCode());
                planMetricService.updateJmeterTestPlanMetric(planMetric);
            }else {
                logger.error("metric {} plan {} end error planMetric={} sample size={}", metric.getSn(),plan.getName(), planMetric,totalSamples.size());
            }

        }catch (Exception ex){
            logger.error("metric {} plan {} end error  sample size={}", metric.getSn(),plan.getName(),totalSamples.size());
        }

        logger.info("metric {} plan {} end callback end @ {}", metric.getSn(),plan.getName(), LocalDateTime.now());
    }


    public void fail(Exception ex){

        running = false;

        JmeterTestPlanMetric planMetric = planMetricService.selectJmeterTestPlanMetricById(this.metric.getId());
        if(planMetric!=null ){
            planMetric.setEndTime(new Date());
            planMetric.setStatus(PlanMetricStatusEnum.FAIL.getCode());
            planMetricService.updateJmeterTestPlanMetric(planMetric);
        }

        logger.error("metric {} plan {} end error planMetric={} sample size={}", metric.getSn(),plan.getName(), planMetric,totalSamples.size(),ex);

    }

}
